<!DOCTYPE html>
<html lang="en">

<head>

    <meta charset="UTF-8">

    <!-- <link rel="apple-touch-icon" type="image/png"
        href="https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" /> -->
    <meta name="apple-mobile-web-app-title" content="CodePen">

    <!-- <link rel="shortcut icon" type="image/x-icon"
        href="https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" /> -->

    <!-- <link rel="mask-icon" type=""
        href="https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg"
        color="#111" /> -->


    <title>CodePen - three.js particle animate</title>

    <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css"> -->



    <style>
        * {
            margin: 0;
        }

        html,
        body {
            position: relative;
            width: 100%;
            height: 100%;
        }

        body {
            background-image: radial-gradient(circle at center, #002764 0%, #131a22 50%);
        }

        canvas {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
        }
    </style>

    <script>
        window.console = window.console || function (t) { };
    </script>



    <script>
        if (document.location.search.match(/type=embed/gi)) {
            window.parent.postMessage("resize", "*");
        }
    </script>


</head>

<body translate="no">

    <script
        src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js"></script>

    <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/77/three.min.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.1.0/Tween.min.js'></script>
    <script id="rendered-js" type="module">
/******/(function (modules) {// webpackBootstrap
  /******/ // The module cache
  /******/var installedModules = {};
  /******/
  /******/ // The require function
  /******/function __webpack_require__(moduleId) {
    /******/
    /******/ // Check if module is in cache
    /******/if (installedModules[moduleId]) {
      /******/return installedModules[moduleId].exports;
                    /******/
                }
    /******/ // Create a new module (and put it into the cache)
    /******/var module = installedModules[moduleId] = {
      /******/i: moduleId,
      /******/l: false,
      /******/exports: {}
                    /******/
                };
    /******/
    /******/ // Execute the module function
    /******/modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/ // Flag the module as loaded
    /******/module.l = true;
    /******/
    /******/ // Return the exports of the module
    /******/return module.exports;
                /******/
            }
  /******/
  /******/
  /******/ // expose the modules object (__webpack_modules__)
  /******/__webpack_require__.m = modules;
  /******/
  /******/ // expose the module cache
  /******/__webpack_require__.c = installedModules;
  /******/
  /******/ // define getter function for harmony exports
  /******/__webpack_require__.d = function (exports, name, getter) {
    /******/if (!__webpack_require__.o(exports, name)) {
      /******/Object.defineProperty(exports, name, {
        /******/configurable: false,
        /******/enumerable: true,
        /******/get: getter
                /******/
            });
                    /******/
                }
                /******/
            };
  /******/
  /******/ // getDefaultExport function for compatibility with non-harmony modules
  /******/__webpack_require__.n = function (module) {
    /******/var getter = module && module.__esModule ?
    /******/function getDefault() { return module['default']; } :
    /******/function getModuleExports() { return module; };
    /******/__webpack_require__.d(getter, 'a', getter);
    /******/return getter;
                /******/
            };
  /******/
  /******/ // Object.prototype.hasOwnProperty.call
  /******/__webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  /******/
  /******/ // __webpack_public_path__
  /******/__webpack_require__.p = "";
  /******/
  /******/ // Load entry module and return exports
  /******/return __webpack_require__(__webpack_require__.s = 0);
            /******/
        })(
/************************************************************************/
/******/[
/* 0 */
/***/function (module, __webpack_exports__, __webpack_require__) {

                    "use strict";
                    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
  /* harmony import */var __WEBPACK_IMPORTED_MODULE_0__time_js__ = __webpack_require__(1);

                    // import THREE from 'three';


                    let waveVertexShader = `
    uniform float size;
    uniform float frequency1;
    uniform float frequency2;
    uniform float offset1;
    uniform float offset2;
    uniform float waveHeight1;
    uniform float waveHeight2;


    void main() { 
        vec3 curPos;
        curPos = vec3(position.x, 1, position.z);
        curPos[1] = cos(position.x * frequency1 + offset1) * sin(position.z * frequency1 + offset1) * waveHeight1 + cos(position.x * frequency2 + offset2) * sin(position.z * frequency2 + offset2) * waveHeight2;


        gl_PointSize = size;//min(size * curPos[1] * 0.2, 4.0);
        gl_Position = projectionMatrix * modelViewMatrix * vec4( curPos, 1.0 ); 
    }

`;

                    let waveFragmentShader = `

    uniform sampler2D texture; 
    uniform vec3 color; 
    uniform float opacity; 
    void main() { 
        
        gl_FragColor = vec4(color, opacity) * texture2D( texture, gl_PointCoord ); 
        // gl_FragColor = vec4(1,1,1,1); 
    }

`;

                    let pointImg = './point.png';
                    let pointCvs = document.createElement('canvas');
                    let pointCtx = pointCvs.getContext('2d');

                    pointCvs.width = 32;
                    pointCvs.height = 32;

                    var grd = pointCtx.createRadialGradient(16, 16, 5, 16, 16, 16);
                    grd.addColorStop(0, 'rgba(255, 255, 255, 0.3)');
                    grd.addColorStop(1, 'rgba(255, 255, 255, 0)');

                    pointCtx.fillStyle = grd;
                    pointCtx.fillRect(0, 0, 32, 32);
                    // document.body.appendChild(pointCvs);

                    class Wave extends __WEBPACK_IMPORTED_MODULE_0__time_js__["a" /* Time */] {
                        constructor(options) {
                            super();

                            let defaults = {
                                color: '#ffffff',
                                opacity: 1,
                                position: new THREE.Vector3(),
                                xCount: 100,
                                zCount: 100,

                                xDis: 200,
                                zDis: 200,

                                size: 1, // 点大小
                                frequency1: 0.2,
                                frequency2: 0.1,

                                maxWaveHeight1: 10,
                                minWaveHeight1: 3,
                                maxWaveHeight2: 8,
                                minWaveHeight2: 5,

                                initOffset1: 0,
                                initOffset2: 0,
                                offsetSpeed1: 2,
                                offsetSpeed2: 4,
                                offsetSign: 1 // -1 or 1
                            };


                            for (let key in defaults) {
                                options[key] = options[key] || defaults[key];
                            }
                            options.xStep = options.xDis / options.xCount;
                            options.zStep = options.zDis / options.zCount;

                            this.options = options;

                            this.tick;

                            this.offset1 = options.initOffset1;
                            this.offset2 = options.initOffset2;

                            this.particlePositions;
                            this.obj = this.create();
                        }

                        create() {
                            let options = this.options;

                            let particlesGeom = new THREE.BufferGeometry();
                            let particlePositions = new Float32Array(options.xCount * options.zCount * 3);

                            let uniforms = {

                                texture: {
                                    value: new THREE.CanvasTexture(pointCvs)
                                },

                                color: {
                                    value: new THREE.Color(options.color)
                                },

                                opacity: {
                                    type: 'float',
                                    value: options.opacity
                                },

                                size: {
                                    type: 'float',
                                    value: options.size * 10
                                },

                                frequency1: {
                                    type: 'float',
                                    value: options.frequency1
                                },

                                frequency2: {
                                    type: 'float',
                                    value: options.frequency2
                                },

                                offset1: {
                                    type: 'float',
                                    value: 0
                                },

                                offset2: {
                                    type: 'float',
                                    value: 0
                                },

                                waveHeight1: {
                                    type: 'float',
                                    value: 0
                                },

                                waveHeight2: {
                                    type: 'float',
                                    value: 0
                                }
                            };





                            var shaderMaterial = new THREE.ShaderMaterial({

                                uniforms: uniforms,
                                vertexShader: waveVertexShader,
                                fragmentShader: waveFragmentShader,

                                blending: THREE.AdditiveBlending,
                                depthTest: false,
                                transparent: true
                            });




                            let count = 0;
                            for (let x = 0; x < options.xCount; x++) {
                                for (let z = 0; z < options.zCount; z++) {

                                    particlePositions[count++] = x * options.xStep;
                                    particlePositions[count++] = 0; // y
                                    particlePositions[count++] = z * options.zStep;
                                }
                            }

                            this.particlePositions = particlePositions;

                            particlesGeom.setDrawRange(0, options.xCount * options.zCount);
                            particlesGeom.addAttribute('position', new THREE.BufferAttribute(particlePositions, 3).setDynamic(true));
                            particlesGeom.computeBoundingBox();
                            particlesGeom.center();

                            let points = new THREE.Points(particlesGeom, shaderMaterial);
                            points.position.copy(options.position);
                            points.rotation.y = Math.random() * 0.2;

                            return points;
                        }

                        start() {
                            this.tick = this.addTick(this.update);

                            let that = this;
                            function changeWHP(waveHeight) {
                                that.obj.material.uniforms.waveHeight1.value = waveHeight.waveHeight1;
                                that.obj.material.uniforms.waveHeight2.value = waveHeight.waveHeight2;
                                // console.log(this.waveHeight1);
                            }

                            let waveHeight = {
                                waveHeight1: this.options.minWaveHeight1,
                                waveHeight2: this.options.minWaveHeight2
                            };

                            let tween1 = new TWEEN.Tween(waveHeight).
                                to({
                                    waveHeight1: this.options.maxWaveHeight1,
                                    waveHeight2: this.options.maxWaveHeight2
                                },
                                    3000).
                                easing(TWEEN.Easing.Cubic.InOut).
                                onUpdate(changeWHP);

                            let tween2 = new TWEEN.Tween(waveHeight).
                                to({
                                    waveHeight1: this.options.minWaveHeight1,
                                    waveHeight2: this.options.minWaveHeight2
                                },
                                    3000).
                                easing(TWEEN.Easing.Cubic.InOut).
                                onUpdate(changeWHP);

                            this.addTween(tween1);
                            // this.addTween(tween2);
                            tween1.chain(tween2);
                            tween2.chain(tween1);
                            tween1.start();

                        }

                        stop() {
                            this.removeTick(this.tick);
                        }

                        update(delta) {
                            let options = this.options;
                            let second = delta / 1000;
                            let particlePositions = this.particlePositions;

                            this.obj.material.uniforms.offset1.value += second * options.offsetSpeed1 * options.offsetSign;
                            this.obj.material.uniforms.offset2.value += second * options.offsetSpeed2 * options.offsetSign;
                        }
                    }


                    class Ani extends __WEBPACK_IMPORTED_MODULE_0__time_js__["a" /* Time */] {

                        constructor() {
                            super();
                            this.waves = [];
                            this.tick;

                            this.scene = new THREE.Scene(); //场景

                            this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); //透视相机
                            this.camera.position.set(0, 6, 150); //相机位置
                            this.scene.add(this.camera); //add到场景中
                            // this.scene.fog = new THREE.Fog(0x000000, 100, 500);

                            this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); //渲染
                            this.renderer.setClearColor(0x000000, 0);
                            this.renderer.setSize(window.innerWidth, window.innerWidth * 9 / 16);
                            document.querySelector('body').appendChild(this.renderer.domElement); //将渲染Element添加到Dom中
                        }

                        resize() {
                            // console.log(1);
                            this.camera.aspect = 16 / 9;
                            this.renderer.setSize(window.innerWidth, window.innerWidth * 9 / 16);
                        }

                        addWave(wave) {
                            this.waves.push(wave);
                            this.scene.add(wave.obj);
                        }

                        start() {
                            this.waves.forEach(w => w.start());
                            this.tick = this.addTick(this.update);
                        }

                        stop() {
                            this.removeTick(this.tick);
                        }

                        update() {
                            this.renderer.render(this.scene, this.camera);
                        }
                    }


                    let wave1 = new Wave({
                        color: 0x3062ff,
                        opacity: 0.7,
                        position: new THREE.Vector3(),
                        xCount: 300,
                        zCount: 300,
                        xDis: 200, // x 宽
                        zDis: 200, // z 宽
                        size: 0.6, // 点大小
                        frequency1: 0.03,
                        frequency2: 0.06,

                        maxWaveHeight1: 8,
                        minWaveHeight1: 3,
                        maxWaveHeight2: 6,
                        minWaveHeight2: 3,

                        initOffset1: 0,
                        initOffset2: 0,
                        offsetSpeed1: 0.6,
                        offsetSpeed2: 0.4,
                        offsetSign: 1 // -1 or 1
                    });
                    let wave2 = new Wave({
                        color: 0x3bdee0,
                        opacity: 0.2,
                        position: new THREE.Vector3(0, 6, -50),
                        xCount: 180,
                        zCount: 180,
                        xDis: 200, // x 宽
                        zDis: 200, // z 宽
                        size: 0.4, // 点大小
                        frequency1: 0.06,
                        frequency2: 0.052,

                        maxWaveHeight1: 6,
                        minWaveHeight1: 4,
                        maxWaveHeight2: 4,
                        minWaveHeight2: 2,

                        initOffset1: 0,
                        initOffset2: 0,
                        offsetSpeed1: 0.3,
                        offsetSpeed2: 0.1,
                        offsetSign: -1 // -1 or 1
                    });




                    let ani = new Ani();
                    ani.addWave(wave1);
                    ani.addWave(wave2);

                    ani.start();


                    window.addEventListener('resize', () => {
                        // console.log(1132);
                        ani.resize();
                    });

                    window.TIME.start();

                    /***/
                },
/* 1 */
/***/function (module, __webpack_exports__, __webpack_require__) {

                    "use strict";
  /* unused harmony export TIME */
  /* harmony export (binding) */__webpack_require__.d(__webpack_exports__, "a", function () { return Time; });
                    // import TWEEN from 'tween.js';

                    /* 时间 */
                    var TIME = {
                        // 所有时间body对象
                        bodys: [],
                        delta: 16
                    };


                    var stop = false;
                    var t;
                    TIME.addBody = function (timeBody) {
                        this.bodys.push(timeBody);
                    };

                    TIME.removeBody = function (timeBody) {
                        var index = this.bodys.indexOf(timeBody);

                        if (index !== -1) {
                            this.bodys.splice(index, 1);
                        }
                    };
                    TIME.tick = function () {
                        var now = new Date().getTime();
                        var last = now;
                        var delta;
                        return function () {
                            delta = now - last;
                            delta = delta > 500 ? 30 : delta < 16 ? 16 : delta;
                            TIME.delta = delta;
                            last = now;

                            TIME.handleFrame(delta);
                            if (!stop) {
                                t = requestAnimationFrame(TIME.tick);
                                // setTimeout(TIME.tick, 1000);
                            }
                        };
                    }();


                    TIME.start = function () {
                        stop = false;
                        cancelAnimationFrame(t);
                        this.tick();
                    };

                    TIME.stop = function () {
                        cancelAnimationFrame(t);
                        stop = true;
                    };

                    TIME.handleFrame = function (delta) {

                        TIME.bodys.forEach(function (body) {
                            if (!body.isStop) {
                                body.ticks.forEach(function (tick) {
                                    tick.fn && tick.fn(delta);
                                });
                            }
                        });

                        TWEEN.update();
                    };

                    window.TIME = TIME;

                    /* 时间物体类，提供两个时机，帧更新，固定间隔更新，每一个有时间概念的物体，就继承 */
                    class Time {

                        constructor() {
                            this.ticks = [];
                            this.tweens = [];
                            this.isStop = false;
                            TIME.addBody(this);
                        }


                        /**
                         * 该物体灭亡
                         */
                        destory() {
                            TIME.removeBody(this);
                        }

                        /** 
                         * 帧更新
                         * @param timegap 与上一帧的时间间隔
                         */
                        addTick(fn) {
                            var tick = { 'fn': fn.bind(this) };

                            tick.isStop = false;
                            this.ticks.push(tick);
                            return tick;
                        }

                        removeTick(tick) {
                            if (!tick) {
                                // remove all
                                this.ticks = [];
                                return;
                            }

                            var index = this.ticks.indexOf(tick);

                            if (index !== -1) {
                                this.ticks.splice(index, 1);
                            }
                        }

                        /** 
                         * tween
                         */
                        addTween(tween) {
                            this.tweens.push(tween);
                        }

                        removeTween(tween) {
                            if (!tween) {
                                // remove all
                                this.tween = [];
                                return;
                            }

                            var index = this.tweens.indexOf(tween);

                            if (index !== -1) {
                                //tween.stop();
                                this.tweens.splice(index, 1);
                            }
                        }

                        // stop 暂停时间
                        stop() {
                            this.isStop = true;
                            this.tweens.forEach(function (tween) {
                                tween.stop();
                            });
                        }

                        start() {
                            this.isStop = false;
                            this.tweens.forEach(function (tween) {
                                tween.start();
                            });
                        }
                    }


                    window.Time = Time;

                    for (let i = 0; i < 10000; i += 100) {
                        window['TIME_' + i] = window.env === 'develop' ? 0 : i;
                    }
                    /***/
                }
/******/]);
//# sourceURL=pen.js
    </script>



</body>

</html>